class Lexer {
    constructor(input) {
        this.input = input;
        this.position = 0;
        this.readPosition = 0;
        this.markPostion = 0;
        this.ch = '';
        this.readChar();
    }

    readChar() {
        if (this.readPosition >= this.input.length) {
            this.ch = null;
        } else {
            this.ch = this.input[this.readPosition];
        }
        this.position = this.readPosition;
        this.readPosition += 1;
    }

    // 读取下一个token
    nextToken() {
        // 声明一个token变量
        let token;
        // 跳过空白字符
        this.skipWhitespace();
        // 根据当前字符ch的类型，创建对应的token
        switch (this.ch) {
            case '=':
                if (this.peekChar() === '=') {
                    const ch = this.ch;
                    this.readChar();
                    token = new Token(TokenType.EQ, ch + this.ch);
                } else {
                    token = new Token(TokenType.ASSIGN, this.ch);
                }
                break;
            case ';':
                // 如果当前字符是';'，则创建一个分号token
                token = new Token(TokenType.SEMICOLON, this.ch);
                break;
            case '(':
                // 如果当前字符是'('，则创建一个左括号token
                token = new Token(TokenType.LPAREN, this.ch);
                break;
            case ')':
                // 如果当前字符是')'，则创建一个右括号token
                token = new Token(TokenType.RPAREN, this.ch);
                break;
            case ',':
                // 如果当前字符是','，则创建一个逗号token
                token = new Token(TokenType.COMMA, this.ch);
                break;
            case '+':
                // 如果当前字符是'+'，则创建一个加号token
                if(this.peekChar() === '+'){
                    const ch = this.ch;
                    this.readChar();
                    token = new Token(TokenType.INCREMENT, ch + this.ch);
                }else {
                    token = new Token(TokenType.PLUS, this.ch);
                }
                break;
            case '-':
                if(this.peekChar() === '-'){
                    const ch = this.ch;
                    this.readChar();
                    token = new Token(TokenType.DECREMENT, ch + this.ch);
                } else if (this.peekChar() === '>') {
                    const ch = this.ch;
                    this.readChar();
                    token = new Token(TokenType.ARROW, ch + this.ch);
                } else {
                    // 如果当前字符是'-'，则创建一个减号token
                    token = new Token(TokenType.MINUS, this.ch);
                }
                break;
            case '*':
                // 如果当前字符是'*'，则创建一个乘号token
                token = new Token(TokenType.ASTERISK, this.ch);
                break;
            case '/':
                // 如果当前字符是'/'，则创建一个除号token
                token = new Token(TokenType.SLASH, this.ch);
                break;
            case '.':
                token= new Token(TokenType.DOT, '.');
                break;
            case '[':
                token = new Token(TokenType.LBRACKET, this.ch);
                break;
            case ']':
                token = new Token(TokenType.RBRACKET, this.ch);
                break;    
            case '{':
                // 如果当前字符是'{'，则创建一个左大括号token
                token = new Token(TokenType.LBRACE, this.ch);
                break;
            case '}':
                // 如果当前字符是'}'，则创建一个右大括号token
                token = new Token(TokenType.RBRACE, this.ch);
                break;
            case '&':
                if (this.peekChar() === '&') {
                    const ch = this.ch;
                    this.readChar();
                    token = new Token(TokenType.AND, ch + this.ch);
                    
                }else{
                    token = new Token(TokenType.BITWISE_AND, this.ch);
                }
                break;
            case '|':
                if(this.peekChar() === '|'){
                    const ch = this.ch;
                    this.readChar();
                    token = new Token(TokenType.OR, ch + this.ch);
                } else {
                    token = new Token(TokenType.BITWISE_OR, this.ch);
                }
                break;
            case '!':
                if(this.peekChar() === '='){
                    const ch = this.ch;
                    this.readChar();
                    token = new Token(TokenType.NOT_EQ, ch + this.ch);
                } else {
                    token = new Token(TokenType.NOT, this.ch);
                }
                break;    
            case '^':
                token = new Token(TokenType.BITWISE_XOR, this.ch);
                break;
            case '~':
                token = new Token(TokenType.BITWISE_NOT, this.ch);
                break;
            case '%':
                token = new Token(TokenType.MODULO, this.ch);
                break;    
            case '>':
                // 如果当前字符是'>'，则判断下一个字符是否为'='
                if (this.peekChar() === '=') {
                    // 如果下一个字符是'='，则创建一个大于等于token
                    const ch = this.ch;
                    this.readChar();
                    token = new Token(TokenType.GE, ch + this.ch);
                }else if (this.peekChar() === '>') {
                    const ch = this.ch;
                    this.readChar();
                    token = new Token(TokenType.SHIFT_RIGHT, ch + this.ch);
                } else {
                    // 如果下一个字符不是'='，则创建一个大于token
                    token = new Token(TokenType.GT, this.ch);
                }
                break;
            case '<':
                // 如果当前字符是'<'，则判断下一个字符是否为'='
                if (this.peekChar() === '=') {
                    // 如果下一个字符是'='，则创建一个小于等于token
                    const ch = this.ch;
                    this.readChar();
                    token = new Token(TokenType.LE, ch + this.ch);
                }else if (this.peekChar() === '<') {
                    const ch = this.ch;
                    this.readChar();
                    token = new Token(TokenType.SHIFT_LEFT, ch + this.ch);
                } else {
                    // 如果下一个字符不是'='，则创建一个小于token
                    token = new Token(TokenType.LT, this.ch);
                }
                break;
            case '"':
                token = new Token(TokenType.STRING, this.readString());
                break; 
            case ':':
                token = new Token(TokenType.COLON, this.ch);
                break;       
            case null:
                // 如果当前字符是null，则创建一个EOF token
                token = new Token(TokenType.EOF, '');
                break;
            default:
                // 如果当前字符不是上述字符，则判断是否为字母或数字
                if (this.isLetter(this.ch)) {
                    // 如果是字母，则读取标识符，并创建一个标识符token
                    const literal = this.readIdentifier();
                    token = new Token(Token.lookupIdent(literal), literal);
                    return token;
                } else if (this.isDigit(this.ch)) {
                    // 如果是数字，则读取数字，并创建一个整数token
                    const number = this.readNumber();
                    token = new Token(number.includes('.') ? TokenType.FLOAT : TokenType.INT, number);
                    return token;
                } else {
                    // 如果既不是字母也不是数字，则创建一个非法token
                    token = new Token(TokenType.ILLEGAL, this.ch);
                }
        }
        // 读取下一个字符
        this.readChar();
        return token;
    }

    skipWhitespace() {
        while (this.ch === ' ' || this.ch === '\t' || this.ch === '\n' || this.ch === '\r') {
            this.readChar();
        }
    }

    readString() {
        const position = this.position + 1;
        while (true) {
            this.readChar();
            if (this.ch === '"' || this.ch === null) {
                break;
            }
        }
        return this.input.slice(position, this.position);
    }

    readIdentifier() {
        const position = this.position;
        while (this.isLetter(this.ch)||this.isDigit(this.ch)) {
            this.readChar();
        }
        return this.input.slice(position, this.position);
    }

    readNumber() {
        const position = this.position;
        let isFloat = false;

        // 读取整数部分
        while (this.isDigit(this.ch)) {
            this.readChar();
        }

        // 检查是否为浮点数
        if (this.ch === '.') {
            isFloat = true;
            this.readChar();

            // 读取小数部分
            while (this.isDigit(this.ch)) {
                this.readChar();
            }
        }

        return this.input.slice(position, this.position);
    }

    isLetter(ch) {
        return (
            ('a' <= ch && ch <= 'z') ||
            ('A' <= ch && ch <= 'Z') ||
            ch === '_' ||
            (/[\u4e00-\u9fa5]/.test(ch)) // 检查是否为中文字符
        );
    }

    isDigit(ch) {
        return ch !== null && '0' <= ch && ch <= '9';
    }

    // 查看下一个字符
    peekChar() {
        // 如果读取位置大于等于输入长度，则返回null
        if (this.readPosition >= this.input.length) {
            return null;
        } else {
            // 否则返回输入中读取位置的字符
            return this.input[this.readPosition];
        }
    }

    mark() {
        this.markPostion = this.position;
    }

    reset() {
        this.position = this.markPostion;
        this.readPosition = this.markPostion;
        this.readChar();
    }
}

const TokenType = {
    ILLEGAL: 'ILLEGAL',
    EOF: 'EOF',
    LET: 'LET',
    IDENT: 'IDENT',
    INT: 'INT',
    FLOAT: 'FLOAT',
    STRING: 'STRING',
    ASSIGN: '=',
    PLUS: '+',
    MINUS: '-',
    ASTERISK: '*',
    SLASH: '/',
    COMMA: ',',
    SEMICOLON: ';',
    LPAREN: '(',
    RPAREN: ')',
    LBRACKET: '[',
    RBRACKET: ']',
    LBRACE: '{',
    RBRACE: '}',
    FUNCTION: 'FUNCTION',
    RETURN: 'RETURN',
    IF: 'IF',
    ELSE_IF: 'ELSE_IF',
    ELSE: 'ELSE',
    TRUE: 'TRUE',
    FALSE: 'FALSE',
    GT: '>',
    LT: '<',
    LE: '<=',
    GE: '>=',
    FOR: 'FOR',
    WHILE: 'WHILE',
    SWITCH: 'SWITCH',
    CASE: 'CASE',
    DEFAULT: 'DEFAULT',
    DOT: '.',
    BITWISE_AND: '&',
    BITWISE_OR: '|',
    BITWISE_XOR: '^',
    BITWISE_NOT: '~',
    AND: '&&',
    OR: '||',
    NOT: '!',
    MODULO: '%',
    SHIFT_LEFT: '<<',
    SHIFT_RIGHT: '>>',
    INCREMENT: '++',
    DECREMENT: '--',
    EQ: '==',
    NOT_EQ: '!=',
    BREAK: 'BREAK',
    COLON: ':',
    PRINT: 'PRINT',
    ARROW:'->'

};

class Token {
    constructor(type, literal) {
        this.type = type;
        this.literal = literal;
    }

    static lookupIdent(ident) {
        const keywords = {
            'fn': TokenType.FUNCTION,
            'let': TokenType.LET,
            'return': TokenType.RETURN,
            'if': TokenType.IF,
            'else if': TokenType.ELSE_IF,
            'else': TokenType.ELSE,
            'true': TokenType.TRUE,
            'false': TokenType.FALSE,
            'for': TokenType.FOR,
            'while': TokenType.WHILE,
            'switch': TokenType.SWITCH,
            'case': TokenType.CASE,
            'default': TokenType.DEFAULT,
            '只因':TokenType.IF,
            'break': TokenType.BREAK,
            'print': TokenType.PRINT,
        };
        return keywords[ident] || TokenType.IDENT;
    }
}

module.exports = {
    Lexer,
    TokenType,
    Token,
};